package com.lin.missyou.service;

import com.lin.missyou.core.LocalUser;
import com.lin.missyou.core.enumeration.OrderStatus;
import com.lin.missyou.core.money.IMoneyDiscout;
import com.lin.missyou.dto.OrderDTO;
import com.lin.missyou.dto.SkuInfoDTO;
import com.lin.missyou.exception.http.ForbiddenException;
import com.lin.missyou.exception.http.NotFoundException;
import com.lin.missyou.exception.http.ParameterException;
import com.lin.missyou.logic.CouponChecker;
import com.lin.missyou.logic.OrderChecker;
import com.lin.missyou.model.*;
import com.lin.missyou.repository.CouponRepository;
import com.lin.missyou.repository.OrderRepository;
import com.lin.missyou.repository.SkuRepository;
import com.lin.missyou.repository.UserCouponRepository;
import com.lin.missyou.util.CommonUtil;
import com.lin.missyou.util.OrderUtil;
import net.bytebuddy.TypeCache;
import org.apache.tomcat.jni.Local;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.jaxb.SpringDataJaxb;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author gcq
 * @Create 2021-11-08
 */
@Service
public class OrderService {

    @Autowired
    private SkuService skuService;

    @Autowired
    private CouponRepository couponRepository;

    @Autowired
    private UserCouponRepository userCouponRepository;

    @Autowired
    private IMoneyDiscout iMoneyDiscout;

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private SkuRepository skuRepository;

    /**
     * 最大购买数量
     */
    @Value("${missyou.order.max-sku-limit}")
    private int maxSkuLimit;
    /**
     * 延迟支付时间
     */
    @Value("${missyou.order.pay-timelimit}")
    private Integer payTimeLimit;

    @Transactional
    public Long placeOrder(Long uid, OrderDTO orderDTO, OrderChecker orderChecker)
    {
        Calendar now = Calendar.getInstance();
        Calendar now1 = (Calendar) now.clone();
        /**
         *  当前时间加上 过期时间 得到订单过期时间 用于在查询订单状态时进行判断
         */
        Date expiredTime = CommonUtil.addSomeSeconds(now,this.payTimeLimit).getTime();

        Order order = Order.builder()
                .orderNo(OrderUtil.makeOrderNo())
                .finalTotalPrice(orderDTO.getFinalTotalPrice())
                .snapImg(orderChecker.getLeaderImg())
                .snapTitle(orderChecker.getLeaderTitle())
                .totalCount(orderChecker.getTotalCount().longValue())
                .totalPrice(orderDTO.getTotalPrice())
                .userId(uid)
                .status(OrderStatus.UNPAID.value())
                .placedTime(now1.getTime())
                .expiredTime(expiredTime)
                .prepayId("1")
                .build();

        order.setSnapAddress(orderDTO.getAddress());
        order.setCreateTime(now.getTime());
        // 保存订单
        this.orderRepository.save(order);
        // 减去库存
        this.reduceStock(orderChecker);
        // 核销优惠券
        if(orderDTO.getCouponId() != null)
        {
            this.writeOffCoupon(orderDTO.getCouponId(), order.getId(), uid);
        }
        // TODO 加入到延迟消息队列
        return order.getId();
    }

    public void writeOffCoupon(Long couponId, Long oid, Long uid)
    {
        int result = this.userCouponRepository.writeOff(couponId, oid, uid);
        if(result != -1)
        {
            throw new ForbiddenException(40012);
        }
    }
    /**
     * 减去库存
     * @param orderChecker
     */
    private void reduceStock(OrderChecker orderChecker)
    {
        List<OrderSku> orderSkuList = orderChecker.getOrderSkuList();
        for (OrderSku orderSku : orderSkuList) {
            int result = skuRepository.reduceStock(orderSku.getId(), orderSku.getCount().longValue());
            if(result != 1)
            {
                throw new ParameterException(50003);
            }
        }
    }


    /**
     * 验证订单
     *
     * @param uid 用户id
     * @param orderDTO 前端传递过来的DTO
     */
    public OrderChecker isOk(Long uid, OrderDTO orderDTO)
    {
        // 前端传递过来的价格小于0 抛出异常
        if(orderDTO.getFinalTotalPrice().compareTo(new BigDecimal("0")) < 0)
        {
            throw new ParameterException(50011);
        }

        // 获取上传的skuId
        List<Long> skuIdList = orderDTO.getSkuInfoList()
                .stream()
                .map(SkuInfoDTO::getId)
                .collect(Collectors.toList());

        // 根据SkuId查询出对应的sku信息
        List<Sku> skuList = skuService.getSkuListByIds(skuIdList);

        Long couponId = orderDTO.getCouponId();
        CouponChecker couponChecker = null;
        if(couponId != null)
        {
            // 优惠卷
            Coupon coupon = couponRepository.findById(couponId)
                    .orElseThrow(() -> new NotFoundException(40004));
            // 优惠卷与用户关联
            UserCoupon userCoupon = userCouponRepository.findFirstByUserIdAndCouponIdAndStatus(uid, couponId, 1)
                    .orElseThrow(() -> new NotFoundException(50006));
            // 进行优惠卷效验
            couponChecker = new CouponChecker(coupon, iMoneyDiscout);
        }
        OrderChecker orderChecker = new OrderChecker(
                orderDTO, skuList, couponChecker, this.maxSkuLimit
        );
        orderChecker.isOk();
        return orderChecker;
    }

    public Page<Order> getUnpaid(Integer page, Integer count) {
        Pageable pageable = PageRequest.of(page, count, Sort.by("createTime").descending());
        Long uid = LocalUser.getUser().getId();
        Date now = new Date();
        return orderRepository.findByStatusAndExpiredTimeGreaterThanAndUserId(OrderStatus.UNPAID.value(), now, uid, pageable);
    }

    public Page<Order> getByStatus(Integer status, Integer page, Integer size)
    {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createTime").descending());
        Long uid = LocalUser.getUser().getId();
        if(status == OrderStatus.ALL.value()){
            return this.orderRepository.findByUserId(uid, pageable);
        }
        return this.orderRepository.findByUserIdAndStatus(uid, status, pageable);
    }

    public Optional<Order> getOrderDetail(Long oid){
        Long uid = LocalUser.getUser().getId();
        return this.orderRepository.findFirstByUserIdAndId(uid, oid);
    }

    public void updateOrderPrepayId(Long orderId, String preOrderId){
        Optional<Order> order = this.orderRepository.findById(orderId);
        // present 如果存在
        order.ifPresent(o -> {
            o.setPrepayId(preOrderId);
            this.orderRepository.save(o);
        });
        order.orElseThrow(() -> new ParameterException(10007));
    }
}